# -*- coding: utf-8 -*-

import os
import yaml
from conf.entity.module.git_config import *
from conf.entity.module.grpc_config import *
from conf.entity.module.oss_config import *
from conf.entity.module.mysql_config import *


def find_project_path(project_name):
    """
    获取当前项目所在目录
    project_name : 工程根目录的名称
    """
    cur_path = os.getcwd()
    project_path = cur_path.split(project_name)[0] + project_name
    return project_path


def get_ldap_config():
    if ldap_config_file is not None:
        user_account = ldap_config_file['ldap']['userAccount']
        user_password = ldap_config_file['ldap']['userPassword']
        return LdapConfig(user_account, user_password)


def get_git_config():
    if git_config_file is not None:
        base_path = git_config_file['git']['repo']['basePath']
        do_merge = git_config_file['git']['repo']['doMerge']
        merge_description = git_config_file['git']['repo']['mergeDescription']
        merge_down = git_config_file['git']['repo']['mergeDown']
        merge_up = git_config_file['git']['repo']['mergeUp']
        branch_pairs = git_config_file['git']['repo']['branchPairs']
        git_config = GitConfig(base_path, merge_description, do_merge, merge_down, merge_up)
        for branch_pair in branch_pairs:
            git_branch_pair = GitBranchPair()
            git_branch_pair.pair_name = branch_pair['pairName']
            git_branch_pair_item = branch_pair['branchPair']
            git_branch_pair.down_branch = git_branch_pair_item['downBranch']
            git_branch_pair.up_branch = git_branch_pair_item['upBranch']
            if 'giteeBaseBranch' in git_branch_pair_item:
                git_branch_pair.gitee_base_branch = git_branch_pair_item['giteeBaseBranch']
            if 'opsBranch' in git_branch_pair_item:
                git_branch_pair.ops_branch = git_branch_pair_item['opsBranch']

            # check Jenkins job name adapters
            if 'jenkinsJobAdapters' in branch_pair:
                jenkins_job_adapters = branch_pair['jenkinsJobAdapters']
                for job_adapter in jenkins_job_adapters:
                    for adapter_name in job_adapter:
                        jenkins_job_adapter = JenkinsJobAdapter(adapter_name)
                        adapter_item = job_adapter[adapter_name]
                        if 'prefix' in adapter_item:
                            jenkins_job_adapter.prefix = adapter_item['prefix']
                        if 'suffix' in adapter_item:
                            jenkins_job_adapter.suffix = adapter_item['suffix']
                        git_branch_pair.add_jenkins_job_adapter(jenkins_job_adapter)

            for project in branch_pair['projects']:
                git_project = GitProject()
                for key in project:
                    item = project[key]
                    git_project.name = item['name']

                    if 'giteePath' in item:
                        git_project.gitee_path = item['giteePath']
                    else:
                        git_project.gitee_path = git_project.name

                    if 'opsCode' in item:
                        git_project.ops_code = item['opsCode']

                    if 'opsOnlyNg' in item:
                        git_project.ops_only_ng = item['opsOnlyNg']
                    elif 'opsOnlyDingTalk' in item:
                        git_project.ops_only_ding_talk = item['opsOnlyDingTalk']

                    if 'jenkinsJob' in item:
                        git_project.jenkins_job = item['jenkinsJob']
                git_project.project_path = f'{base_path}/{git_project.name}'
                git_branch_pair.add_project(git_project)

            git_config.add_branch_pair(git_branch_pair)

        # check Gitee config
        if 'gitee' in git_config_file['git']:
            gitee_f = git_config_file['git']['gitee']
            gitee_config = GiteeConfig()
            gitee_config.access_token = gitee_f['accessToken']
            gitee_config.owner = gitee_f['owner']
            gitee_config.pull_request_title = gitee_f['pullRequest']['title']
            gitee_config.do_pull = gitee_f['pullRequest']['doPull']
            git_config.set_gitee_config(gitee_config)

        # Check Git tag config
        if 'tag' in git_config_file['git']:
            tag_f = git_config_file['git']['tag']
            tag_name = tag_f['name']
            tag_operate_type = tag_f['operateType']
            tag_config = GitTagConfig(tag_name, tag_operate_type)
            if 'comment' in tag_f:
                tag_config.comment = tag_f['comment']
            if 'doOperate' in tag_f:
                tag_config.do_operate = tag_f['doOperate']
            git_config.set_tag_config(tag_config)

        if ldap_config_file is not None:
            ldap_config = get_ldap_config()

            # check ops config
            if 'ops' in git_config_file['git']:
                ops_config = OpsConfig(ldap_config)
                ops_item = git_config_file['git']['ops']
                ops_config.do_make_request = ops_item['doMakeRequest']
                ops_config.request_description = ops_item['description']

                if 'opsNg' in ops_item:
                    ops_config.ops_ng = ops_item['opsNg']
                if 'opsDingtalk' in ops_item:
                    ops_config.ops_dingtalk = ops_item['opsDingtalk']

                git_config.set_ops_config(ops_config)

            # check Jenkins config
            if 'jenkins' in git_config_file['git']:
                jenkins_config = JenkinsConfig(ldap_config)
                jenkins_item = git_config_file['git']['jenkins']
                jenkins_config.base_url = jenkins_item['baseUrl']
                jenkins_config.do_deploy = jenkins_item['doDeploy']

                if 'forceDeploy' in jenkins_item:
                    force_deploy = jenkins_item['forceDeploy']
                    jenkins_config.force_deploy = force_deploy

                git_config.set_jenkins_config(jenkins_config)
        return git_config


class GrpcConfigLoader(object):
    config_file: dict
    common_project_path: str
    grpc_config: GrpcConfig
    mapping_config: GrpcMappingConfig

    def __init__(self, config_file):
        self.config_file = config_file

    def do_load_proto_config(self):
        self.common_project_path = self.config_file['project']['projectPath']

        yml_proto_config = self.config_file['project']['proto']
        proto_config = ProtoConfig()
        proto_config.package = yml_proto_config['package']
        proto_config.java_package = yml_proto_config['javaPackage']
        if 'projectPath' in yml_proto_config:
            proto_config.project_path = yml_proto_config['projectPath']
        else:
            proto_config.project_path = self.common_project_path
        proto_config.root_path = yml_proto_config['rootPath']
        proto_config.file_name = yml_proto_config['fileName']
        proto_config.service_suffix = yml_proto_config['serviceSuffix']
        proto_config.request_suffix = yml_proto_config['requestSuffix']
        proto_config.response_suffix = yml_proto_config['responseSuffix']
        proto_config.message_prefix = yml_proto_config['messagePrefix']
        proto_config.enable_multiple_files = yml_proto_config['enableMultipleFiles']
        if 'moduleLeadingMethods' in yml_proto_config:
            module_leading_methods = yml_proto_config['moduleLeadingMethods']
            if type(module_leading_methods) == list:
                proto_config.add_module_leading_methods(module_leading_methods)
            else:
                raise ValueError(
                    f'moduleLeadingMethods must be defined as arrays, got {type(module_leading_methods)}')
        if 'servant' in yml_proto_config:
            grpc_servant_config = yml_proto_config['servant']
            grpc_servant = GrpcServant()
            grpc_servant.root_path = grpc_servant_config['rootPath']
            grpc_servant.root_package = grpc_servant_config['rootPackage']
            grpc_servant.servant_path = grpc_servant_config['servantPath']
            grpc_servant.servant_suffix = grpc_servant_config['servantSuffix']
            if 'exceptionHandlerPath' in grpc_servant_config:
                grpc_servant.exception_handler_path = grpc_servant_config['exceptionHandlerPath']
            if 'exceptionHandlerName' in grpc_servant_config:
                grpc_servant.exception_handler_name = grpc_servant_config['exceptionHandlerName']
            proto_config.servant = grpc_servant

        # Part switch, must configure
        yml_switch_config = self.config_file['project']['switches']
        gen_from_ice = yml_switch_config['genProtoFromIce']
        gen_from_java = yml_switch_config['genProtoFromJava']
        convert_java_grpc = yml_switch_config['convertGrpcAndJava']
        transfer_servant = yml_switch_config['transferServant']
        document_lark = yml_switch_config['makeIce2GrpcLarkDocument']
        self.grpc_config = GrpcConfig(proto_config, gen_from_ice, gen_from_java,
                                      convert_java_grpc, transfer_servant,
                                      document_lark)
        if 'testMode' in yml_switch_config:
            self.grpc_config.set_test_mode(yml_switch_config['testMode'])

    def do_load_ice_config(self):
        if 'ice' in self.config_file['project']['source']:
            yml_ice_config = grpc_config_file['project']['source']['ice']
            if 'projectPath' in yml_ice_config:
                ice_project_path = yml_ice_config['projectPath']
            else:
                ice_project_path = self.common_project_path
            ice_root_path = yml_ice_config['rootPath']
            ice_file_name = yml_ice_config['fileName']
            ice_servant = yml_ice_config['servant']
            servant_path = ice_servant['path']
            servant_suffix = ice_servant['suffix']
            ice_config = IceConfig(ice_project_path, ice_root_path, ice_file_name, servant_path, servant_suffix)
            self.grpc_config.set_ice_config(ice_config)

            # ice & proto mappings, optional
            if 'mappings' in yml_ice_config:
                mapping_split_tag = '@@'
                mappings = yml_ice_config['mappings']

                def iterate_mapping_config(elements: List[str], module, mapping_type: GrpcMappingTypeEnum, parent):
                    for item in elements:
                        if mapping_split_tag in item:
                            item_split = item.split(mapping_split_tag)
                            if len(item_split) == 2:
                                the_ice_type = item_split[0]
                                the_grpc_type = item_split[1]
                                if parent is not None:
                                    the_ice_type = f'{parent.ice}@{the_ice_type}'
                                    the_grpc_type = f'{parent.grpc}@{the_grpc_type}'
                                self.mapping_config.add_mapping(GrpcMapping(the_ice_type, the_grpc_type,
                                                                module, mapping_type))
                            else:
                                raise RuntimeError(
                                    f'mapping should be separated by {mapping_split_tag}, the mapping: {item}')

                def handle_ice2proto_mapping(mapping, keyword: str, mapping_type: GrpcMappingTypeEnum):
                    if keyword in mapping:
                        elements = mapping[keyword]
                        if keyword == 'services':
                            for dict_item in elements:
                                the_module = None
                                the_ice = None
                                the_grpc = None
                                if 'module' in dict_item:
                                    the_module = dict_item['module']
                                if 'ice' in dict_item:
                                    the_ice = dict_item['ice']
                                if 'grpc' in dict_item:
                                    the_grpc = dict_item['grpc']
                                service_mapping = GrpcMapping(the_ice, the_grpc, the_module, mapping_type)
                                self.mapping_config.add_mapping(service_mapping)
                                if 'methodMappings' in dict_item:
                                    iterate_mapping_config(dict_item['methodMappings'],
                                                           the_module, GrpcMappingTypeEnum.METHOD, service_mapping)
                        elif keyword == 'structs':
                            iterate_mapping_config(elements, None, mapping_type, None)
                        elif keyword == 'servants':
                            for servant_item in elements:
                                if mapping_split_tag not in servant_item:
                                    continue
                                item_split = servant_item.split(mapping_split_tag)
                                if len(item_split) == 2:
                                    ice_servant = item_split[0]
                                    grpc_servant = item_split[1]
                                    servant_mapping = GrpcMapping(ice_servant, grpc_servant, '', mapping_type)
                                    self.mapping_config.add_mapping(servant_mapping)
                                else:
                                    hint = f'mapping should be separated by {mapping_split_tag}: {servant_item}'
                                    raise RuntimeError(hint)

                handle_ice2proto_mapping(mappings, 'structs', GrpcMappingTypeEnum.STRUCT)
                handle_ice2proto_mapping(mappings, 'services', GrpcMappingTypeEnum.SERVICE)
                handle_ice2proto_mapping(mappings, 'servants', GrpcMappingTypeEnum.SERVANT)

    def do_load_java_config(self):
        if 'java' in self.config_file['project']['source']:
            yml_java_config = self.config_file['project']['source']['java']
            java_config = JavaConfig()
            if 'projectPath' in yml_java_config:
                java_config.project_path = yml_java_config['projectPath']
            else:
                java_config.project_path = self.common_project_path
            java_config.root_path = yml_java_config['rootPath']
            java_config.domain_path = yml_java_config['domainPath']
            java_config.domains = yml_java_config['domains']
            self.grpc_config.set_java_config(java_config)

    def do_load_grpc_and_domain_converter_config(self):
        if 'grpcJavaConverter' in self.config_file['project']:
            yml_converter_config = self.config_file['project']['grpcJavaConverter']
            converter_config = GrpcAndDomainConverterConfig()
            if 'projectPath' in yml_converter_config:
                converter_config.project_path = yml_converter_config['projectPath']
            else:
                converter_config.project_path = self.common_project_path
            converter_config.root_path = yml_converter_config['rootPath']
            converter_config.root_package = yml_converter_config['rootPackage']
            converter_config.converter_path = yml_converter_config['converterPath']
            converter_config.domain_path = yml_converter_config['domainPath']
            converter_config.default_suffix = yml_converter_config['defaultSuffix']

            self.grpc_config.set_converter_config(converter_config)

    def load_grpc_config(self):
        if self.config_file is None:
            return

        # must configure proto
        self.do_load_proto_config()

        # init mapping config
        self.mapping_config = GrpcMappingConfig()
        self.grpc_config.set_mapping_config(self.mapping_config)

        # init source config
        if 'source' in grpc_config_file['project']:
            # Part ice, optional
            self.do_load_ice_config()
            # Part Java, optional
            self.do_load_java_config()

        # init converter config
        self.do_load_grpc_and_domain_converter_config()

        return self.grpc_config


def get_grpc_config() -> GrpcConfig:
    return GrpcConfigLoader(grpc_config_file).load_grpc_config()


def get_oss_config():
    if oss_config_file is not None:
        oss_config = OssConfig()
        oss_config.access_key_id = oss_config_file['oss']['accessKeyId']
        oss_config.access_key_secret = oss_config_file['oss']['accessKeySecret']
        oss_config.endpoint = oss_config_file['oss']['endpoint']
        oss_config.bucket_name = oss_config_file['oss']['bucketName']
        return oss_config


def get_liquibase_config():
    if mysql_config_file is not None:
        lb_config = LiquibaseConfig()
        lb_config.root_path = mysql_config_file['project']['liquibase']['rootPath']
        lb_config.ding_talk_path = mysql_config_file['project']['liquibase']['dingTalkPath']
        lb_config.ng_path = mysql_config_file['project']['liquibase']['ngPath']
        lb_config.target = mysql_config_file['project']['liquibase']['target']
        return lb_config


def get_mysql_config():
    if mysql_config_file is not None:
        mysql_config = MySqlConfig()
        mysql_config.url = mysql_config_file['database']['url']
        mysql_config.db_name = mysql_config_file['database']['dbName']
        mysql_config.user_name = mysql_config_file['database']['userName']
        mysql_config.password = mysql_config_file['database']['password']
        return mysql_config


def get_url_perm_config():
    if url_perm_config_file is not None:
        url_perm_config = BasePathConfig()
        url_perm_config.project_path = url_perm_config_file['project']['projectPath']
        url_perm_config.root_path = url_perm_config_file['project']['rootPath']
        return url_perm_config


# do load config
root_path = find_project_path('maycur-pytools')
config_root_path = f'{root_path}/conf/module'
app_yml_path = f'{root_path}/conf/app.yml'

with open(app_yml_path, 'r') as f:
    config = yaml.safe_load(f)

# Git配置
git_active = config['git']['active']
if type(git_active) != bool:
    git_yml_path = f'{config_root_path}/git/git-{git_active}.yml'
    if git_yml_path is not None:
        with open(git_yml_path, 'r') as git_f:
            git_config_file = yaml.safe_load(git_f)

# gRPC配置
grpc_active = config['grpc']['active']
if type(grpc_active) != bool:
    grpc_yml_path = f'{config_root_path}/grpc/grpc-{grpc_active}.yml'
    if grpc_yml_path is not None:
        with open(grpc_yml_path, 'r') as grpc_f:
            grpc_config_file = yaml.safe_load(grpc_f)

# LDAP配置
ldap_active = config['ldap']['active']
ldap_config_file = None
if type(ldap_active) != bool:
    ldap_yml_path = f'{config_root_path}/ldap/ldap-{ldap_active}.yml'
    if ldap_yml_path is not None:
        with open(ldap_yml_path, 'r') as ldap_f:
            ldap_config_file = yaml.safe_load(ldap_f)

# OSS配置
oss_active = config['oss']['active']
if type(oss_active) != bool:
    oss_yml_path = f'{config_root_path}/oss/oss-{oss_active}.yml'
    if oss_yml_path is not None:
        with open(oss_yml_path, 'r') as oss_f:
            oss_config_file = yaml.safe_load(oss_f)

# Database配置
mysql_active = config['mysql']['active']
if type(mysql_active) != bool:
    mysql_yml_path = f'{config_root_path}/database/mysql-{mysql_active}.yml'
    if mysql_yml_path is not None:
        with open(mysql_yml_path, 'r') as mysql_f:
            mysql_config_file = yaml.safe_load(mysql_f)


# URL级别权限配置
url_perm_active = config['url-perm']['active']
if type(url_perm_active) != bool:
    url_perm_yml_path = f'{config_root_path}/urlperm/url-perm-{url_perm_active}.yml'
    if url_perm_yml_path is not None:
        with open(url_perm_yml_path, 'r') as url_perm_f:
            url_perm_config_file = yaml.safe_load(url_perm_f)
